package cn.zf233.xcloud.service.impl;

import cn.zf233.xcloud.config.common.ElasticSearchConfig;
import cn.zf233.xcloud.config.common.FileEsDao;
import cn.zf233.xcloud.config.common.RabbitMQConfig;
import cn.zf233.xcloud.entity.File;
import cn.zf233.xcloud.entity.User;
import cn.zf233.xcloud.mapper.FileMapper;
import cn.zf233.xcloud.service.SearchService;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.HighlightQuery;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Created by zf233 on 2021/4/13
 * 基于 ElasticSearch 的搜索服务
 */
@Service
@ConditionalOnMissingBean(RabbitMQConfig.class)
@ConditionalOnBean(ElasticSearchConfig.class)
public class SearchDefaultServiceImpl implements SearchService, InitializingBean, DisposableBean {

    private static final String FIELD_OLD_FILE_NAME = "oldFileName";
    private static final String FIELD_USER_ID = "userId";
    private static final String FIELD_DELETE_FLAG = "deleteFlag";
    private static final String FIELD_CLASSIFY = "classify";
    private static final String FIELD_FOLDER = "folder";
    private static final String HIGH_LIGHT_PREFIX = "<span style='color:#A9A9A9'>";
    private static final String HIGH_LIGHT_SUFFIX = "</span>";

    @Resource
    private FileMapper fileMapper;

    @Autowired
    private ElasticsearchRestTemplate template;

    @Resource
    private FileEsDao fileEsDao;

    @Override
    public void buildIndex() {
        if (!template.indexOps(File.class).exists()) {
            template.indexOps(File.class).create();
            template.indexOps(File.class).putMapping();
        }
        List<File> fileList = fileMapper.selectFiles();
        fileList.removeIf(file -> file.getParentId() == -1);
        if (!fileList.isEmpty()) {
            insertBatch(fileList);
        }
    }

    @Override
    public void clearIndex() {
        if (template.indexOps(File.class).exists()) {
            template.indexOps(File.class).delete();
        }
    }

    @Override
    public List<File> search(User user, String keyword) {
        CriteriaQuery query = new CriteriaQuery(
                new Criteria()
                        .and(new Criteria(FIELD_OLD_FILE_NAME).matchesAll(keyword))
                        .and(new Criteria(FIELD_USER_ID).is(user.getId()))
                        .and(new Criteria(FIELD_DELETE_FLAG).is(0))
        );

        // 高亮
        HighlightBuilder builder = new HighlightBuilder()
                .field(FIELD_OLD_FILE_NAME)
                .preTags(HIGH_LIGHT_PREFIX)
                .postTags(HIGH_LIGHT_SUFFIX);
        HighlightQuery highlightQuery = new HighlightQuery(builder);
        query.setHighlightQuery(highlightQuery);

        // 查询
        SearchHits<File> search = template.search(query, File.class);

        // 包装击中目标
        return search.stream().map(fileSearchHit -> {
            File file = fileSearchHit.getContent();
            file.setHighLightFileName(
                    fileSearchHit.getHighlightFields()
                            .get(FIELD_OLD_FILE_NAME)
                            .toString()
                            .replace("[", "")
                            .replace("]", ""));
            return file;
        }).collect(Collectors.toList());
    }

    @Override
    public List<File> search(CriteriaQuery query) {
        SearchHits<File> search = template.search(query, File.class);
        return search.stream().map(SearchHit::getContent).collect(Collectors.toList());
    }

    @Override
    public List<File> classify(User user, Integer classify) {
        CriteriaQuery query = new CriteriaQuery(
                new Criteria()
                        .and(new Criteria(FIELD_USER_ID).is(user.getId()))
                        .and(new Criteria(FIELD_CLASSIFY).is(classify))
                        .and(new Criteria(FIELD_DELETE_FLAG).is(0))
        );
        SearchHits<File> search = template.search(query, File.class);
        return search.stream().map(SearchHit::getContent).collect(Collectors.toList());
    }

    @Override
    public List<File> recycle(User user) {
        CriteriaQuery query = new CriteriaQuery(
                new Criteria()
                        .and(new Criteria(FIELD_USER_ID).is(user.getId()))
                        .and(new Criteria(FIELD_FOLDER).is(0))
                        .and(new Criteria(FIELD_DELETE_FLAG).is(1))
        );
        SearchHits<File> search = template.search(query, File.class);
        return search.stream().map(SearchHit::getContent).collect(Collectors.toList());
    }

    @Override
    public void insertBatch(List<File> fileList) {
        fileEsDao.saveAll(fileList);
    }

    @Override
    public void deleteBatch(List<File> fileList) {
        fileEsDao.deleteAll(fileList);
    }

    @Override
    public void insertOrUpdateOne(File file) {
        fileEsDao.save(file);
    }

    @Override
    public void deleteOne(File file) {
        fileEsDao.delete(file);
    }

    @Override
    public void afterPropertiesSet() {
        buildIndex();
    }

    @Override
    public void destroy() {
        clearIndex();
    }
}